import json

import gmsh


class IMshReader:
    standard = None
    file_path = ""
    model_data = None
    type_map = {
        3: "QUAD4",
        2: "CTRIA3",
        15: "POINT1",
        4: "CTETRA",
        5: "CHEXA",
        6: "CPENTA",
        1: "CBAR",
    }

    def __init__(self):
        self.standard = {
            "blocks": [],
            "nodes": [],
            "nodeIds": [],
            "elements": [],
        }

    def read(self, file_path):
        self.file_path = file_path
        version = self.get_version_tag()
        print("version", version)
        gmsh.initialize()
        gmsh.open(file_path)
        # 获取所有节点
        nodeTags, nodeCoords, _ = gmsh.model.mesh.getNodes()
        print("Number of nodes:", len(nodeTags))
        for k in nodeTags:
            self.parse_node(k)

        # 获取所有物理组（维度, 标签）
        phys_groups = gmsh.model.getPhysicalGroups()
        for dim, tag in phys_groups:
            name = gmsh.model.getPhysicalName(dim, tag)
            self.standard["blocks"].append(
                {
                    "name": name,
                    "bid": tag,
                    "dim": dim,
                }
            )

        # 获取所有单元（元素）
        types, elementTags, nodeTags = gmsh.model.mesh.getElements()
        all_element_tags = []
        for elems in elementTags:
            all_element_tags.extend(elems)
        for ele in all_element_tags:
            self.parse_element(ele)

        # 获取所有视图
        view_tags = gmsh.view.getTags()
        if len(view_tags) > 0:
            names = self.get_model_data_name()
        for tag in view_tags:
            self.parse_model_data(tag, 0)
        gmsh.finalize()

    def parse_element(self, elem_tag):
        elem: tuple = gmsh.model.mesh.getElement(elem_tag)
        # print("类型值", elem[0])
        if elem[0] not in self.type_map:
            return
        elem_type = self.type_map[elem[0]]
        res = None
        if (
            elem_type == "QUAD4"
            or elem_type == "CTRIA3"
            or elem_type == "POINT1"
            or elem_type == "CBAR"
        ):
            res = {
                "type": elem_type,
                "eid": int(elem_tag),
                "nodeIds": elem[1].tolist(),
                "dim": int(elem[2]),
                "physicalTag": int(elem[3]),
            }
        elif elem_type == "CTETRA":
            res = {
                "type": elem_type,
                "eid": int(elem_tag),
                "nodeIds": elem[1].tolist(),
                "dim": int(elem[2]),
                "physicalTag": int(elem[3]),
                "faces": [
                    [1, 2, 3],
                    [0, 2, 3],
                    [0, 1, 3],
                    [0, 1, 2],
                ],
            }
        elif elem_type == "CHEXA":
            res = {
                "type": elem_type,
                "eid": int(elem_tag),
                "nodeIds": elem[1].tolist(),
                "dim": int(elem[2]),
                "physicalTag": int(elem[3]),
                "faces": [
                    [0, 1, 2, 3],
                    [4, 5, 6, 7],
                    [0, 1, 5, 4],
                    [1, 2, 6, 5],
                    [2, 3, 7, 6],
                    [3, 0, 4, 7],
                ],
            }
        elif elem_type == "CPENTA":
            res = {
                "type": elem_type,
                "eid": int(elem_tag),
                "nodeIds": elem[1].tolist(),
                "dim": int(elem[2]),
                "physicalTag": int(elem[3]),
                "faces": [
                    [0, 1, 2],
                    [3, 4, 5],
                    [0, 1, 4, 3],
                    [1, 2, 5, 4],
                    [2, 0, 3, 5],
                ],
            }
        self.standard["elements"].append(res)

    def parse_model_data(self, tag, flag: int):
        try:
            r = gmsh.view.getModelData(tag, flag)
            print(r)
            self.parse_model_data(tag, flag + 1)
        except WindowsError:
            print("no data")
        except Exception:
            print("no more data")

    def parse_node(self, node_tag):
        node = gmsh.model.mesh.getNode(node_tag)
        self.standard["nodes"].append(float(node[0][0]))
        self.standard["nodes"].append(float(node[0][1]))
        self.standard["nodes"].append(float(node[0][2]))
        self.standard["nodeIds"].append(int(node_tag))

    def get_version_tag(self):
        start_tag = None
        version = None
        with open(self.file_path, "r", encoding="utf-8") as file:
            for line in file:
                s = line.strip()  # 使用 strip() 去除换行符
                if s.find("$MeshFormat") != -1:
                    start_tag = 1
                elif start_tag == 1:
                    version = s.split(" ")
                    break
        return version

    def get_model_data_name(self):
        start_tag = None
        tmp_count = 0
        result = []
        with open(self.file_path, "r", encoding="utf-8") as file:
            for line in file:
                s = line.strip()  # 使用 strip() 去除换行符
                if s.find("$NodeData") != -1 or s.find("$ElementData") != -1:
                    start_tag = 1
                elif start_tag == 1 and tmp_count != 1:
                    tmp_count += 1
                elif start_tag == 1 and tmp_count == 1:  # 第二行是名字
                    start_tag = None
                    tmp_count = 0
                    result.append(s)
        # 因为时间步的存在 需要去重
        result = list(set(result))
        return result

    def to_json(self):
        return json.dumps(self.standard)
